package com.zhouxiaoge.antlr4.json;

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeProperty;
import org.antlr.v4.runtime.tree.ParseTreeWalker;

/*
{
    "description" : "An imaginary server config file",
    "logs" : {"level":"verbose", "dir":"/var/log"},
    "host" : "antlr.org",
    "admin": ["parrt", "tombu"],
    "aliases": []
}

to

<description>An imaginary server config file</description>
<logs>
    <level>verbose</level>
    <dir>/var/log</dir>
</logs>
<host>antlr.org</host>
<admin>
    <element>parrt</element> <!-- inexact -->
    <element>tombu</element>
</admin>
<aliases></aliases>
 */

public class JSON2XML {
    public static class XMLEmitter extends JSONBaseListener {
        ParseTreeProperty<String> xml = new ParseTreeProperty<>();

        private String getXML(ParseTree ctx) {
            return xml.get(ctx);
        }

        private void setXML(ParseTree ctx, String s) {
            xml.put(ctx, s);
        }

        @Override
        public void enterJson(JSONParser.JsonContext ctx) {
            ParseTree child = ctx.getChild(0);
            String text = child.getText();
            super.enterJson(ctx);
        }

        @Override
        public void exitJson(JSONParser.JsonContext ctx) {
            String xml1 = getXML(ctx.getChild(0));
            setXML(ctx, xml1);
            System.out.println("exitJson-->" + xml1);
        }

        @Override
        public void exitAnObject(JSONParser.AnObjectContext ctx) {
            StringBuilder buf = new StringBuilder();
            buf.append("\n");
            for (JSONParser.PairContext pctx : ctx.pair()) {
                String xml1 = getXML(pctx);
                buf.append(xml1);
            }
            setXML(ctx, buf.toString());
            System.out.println("exitAnObject-->" + buf);
        }

        @Override
        public void exitEmptyObject(JSONParser.EmptyObjectContext ctx) {
            setXML(ctx, "");
            System.out.println("exitEmptyObject-->" + "");
        }

        @Override
        public void exitArrayOfValues(JSONParser.ArrayOfValuesContext ctx) {
            StringBuilder buf = new StringBuilder();
            buf.append("\n");
            for (JSONParser.ValueContext vctx : ctx.value()) {
                buf.append("<element>"); // conjure up element for valid XML
                buf.append(getXML(vctx));
                buf.append("</element>");
                buf.append("\n");
            }
            setXML(ctx, buf.toString());
            System.out.println("exitArrayOfValues-->" + buf);
        }

        @Override
        public void exitEmptyArray(JSONParser.EmptyArrayContext ctx) {
            setXML(ctx, "");
            System.out.println("exitEmptyArray-->" + "");
        }

        @Override
        public void exitPair(JSONParser.PairContext ctx) {
            String tag = stripQuotes(ctx.STRING().getText());
            JSONParser.ValueContext vctx = ctx.value();
            String x = String.format("<%s>%s</%s>\n", tag, getXML(vctx), tag);
            setXML(ctx, x);
            System.out.println("exitPair-->" + x);
        }

        @Override
        public void exitObjectValue(JSONParser.ObjectValueContext ctx) {
            // analogous to String value() {return object();}
            String xml1 = getXML(ctx.object());
            setXML(ctx, xml1);
            System.out.println("exitObjectValue-->" + xml1);
        }

        @Override
        public void exitArrayValue(JSONParser.ArrayValueContext ctx) {
            String xml1 = getXML(ctx.array());
            setXML(ctx, xml1); // String value() {return array();}
            System.out.println("exitArrayValue-->" + xml1);
        }

        @Override
        public void exitAtom(JSONParser.AtomContext ctx) {
            String text = ctx.getText();
            setXML(ctx, text);
            System.out.println("exitAtom-->" + text);
        }

        @Override
        public void exitString(JSONParser.StringContext ctx) {
            String stripQuotes = stripQuotes(ctx.getText());
            setXML(ctx, stripQuotes);
            System.out.println("exitString-->" + stripQuotes);
        }

        public static String stripQuotes(String s) {
            if (s == null || s.charAt(0) != '"') {
                return s;
            }
            return s.substring(1, s.length() - 1);
        }
    }

    public static void main(String[] args) {
        String json = """
                {
                    "description" : "An imaginary server config file",
                    "logs" : {"level":"verbose", "dir":"/var/log"},
                    "host" : "antlr.org",
                    "admin": ["parrt", "tombu"],
                    "aliases": []
                }
                """;
        CharStream charStream = CharStreams.fromString(json);
        JSONLexer lexer = new JSONLexer(charStream);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        JSONParser parser = new JSONParser(tokens);
        parser.setBuildParseTree(true);
        ParseTree tree = parser.json();
        // show tree in text form
//        System.out.println(tree.toStringTree(parser));

        ParseTreeWalker walker = new ParseTreeWalker();
        XMLEmitter converter = new XMLEmitter();
        walker.walk(converter, tree);
        System.out.println(converter.getXML(tree));
    }
}
